﻿Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms
    ''' <summary>
    ''' Encapsulates control that visualy displays certain integer value and allows user to change it within desired range. It imitates <see cref="System.Windows.Forms.TrackBar"/> as far as mouse usage is concerned.
    ''' </summary>
    <ToolboxBitmap(GetType(TrackBar)), _
     DefaultEvent("Scroll"), DefaultProperty("BarColor")> _
    Public Class colorSlider
        Inherits Control
#Region "Events"

        ''' <summary>
        ''' Fires when Slider position has changed
        ''' </summary>
        <Description("Event fires when the Value property changes"), _
         Category("Action")> _
        Public Event ValueChanged As EventHandler

        ''' <summary>
        ''' Fires when user scrolls the Slider
        ''' </summary>
        <Description("Event fires when the Slider position is changed"), _
         Category("Behavior")> _
        Public Event Scroll As ScrollEventHandler

#End Region

#Region "Properties"

        Private _thumbRect As Rectangle
        ''' <summary>
        ''' Gets the thumb rect. Usefull to determine bounding rectangle when creating custom thumb shape.
        ''' </summary>
        ''' <value>The thumb rect.</value>
        <Browsable(False)> _
        Public ReadOnly Property ThumbRect() As Rectangle
            Get
                Return _thumbRect
            End Get
        End Property

        Private barRect As Rectangle
        Private barHalfRect As Rectangle
        Private thumbHalfRect As Rectangle
        Private elapsedRect As Rectangle

    Private _thumbSize As Integer = 8
        ''' <summary>
        ''' Gets or sets the size of the thumb.
        ''' </summary>
        ''' <value>The size of the thumb.</value>
        ''' <exception cref="T:System.ArgumentOutOfRangeException">exception thrown when value is lower than zero or grather than half of appropiate dimension</exception>
    <Description("Set Slider thumb size"), _
     Category("ColorSlider"), _
     DefaultValue(8)> _
    Public Property ThumbSize() As Integer
        Get
            Return _thumbSize
        End Get
        Set(ByVal value As Integer)
            If value > 0 And value < (ClientRectangle.Width) Then
                _thumbSize = value
            Else
                Throw New ArgumentOutOfRangeException("TrackSize has to be greather than zero and lower than half of Slider width")
            End If
            Invalidate()
        End Set
    End Property

        Private _thumbRoundRectSize As New Size(2, 2)
        ''' <summary>
        ''' Gets or sets the size of the thumb round rectangle edges.
        ''' </summary>
        ''' <value>The size of the thumb round rectangle edges.</value>
        <Description("Set Slider's thumb round rect size"), _
         Category("ColorSlider"), _
         DefaultValue(GetType(Size), "2; 2")> _
        Public Property ThumbRoundRectSize() As Size
            Get
                Return _thumbRoundRectSize
            End Get
            Set(ByVal value As Size)
                Dim h As Integer = value.Height, w As Integer = value.Width
                If h <= 0 Then
                    h = 1
                End If
                If w <= 0 Then
                    w = 1
                End If
                _thumbRoundRectSize = New Size(w, h)
                Invalidate()
            End Set
        End Property

        Private _trackerValue As Integer = 50
        ''' <summary>
        ''' Gets or sets the value of Slider.
        ''' </summary>
        ''' <value>The value.</value>
        ''' <exception cref="T:System.ArgumentOutOfRangeException">exception thrown when value is outside appropriate range (min, max)</exception>
        <Description("Set Slider value"), _
         Category("ColorSlider"), _
         DefaultValue(50)> _
        Public Property Value() As Integer
            Get
                Return _trackerValue
            End Get
            Set(ByVal value As Integer)
                If value >= _barMinimum And value <= _barMaximum Then
                    _trackerValue = value
                If Not ValueChangedEvent Is Nothing Then
                    RaiseEvent ValueChanged(Me, New EventArgs())
                End If
                    Invalidate()
                Else
                    Throw New ArgumentOutOfRangeException("Value is outside appropriate range (min, max)")
                End If
            End Set
        End Property

        Private _barMinimum As Integer = 0
        ''' <summary>
        ''' Gets or sets the minimum value.
        ''' </summary>
        ''' <value>The minimum value.</value>
        ''' <exception cref="T:System.ArgumentOutOfRangeException">exception thrown when minimal value is greather than maximal one</exception>
        <Description("Set Slider minimal point"), _
         Category("ColorSlider"), _
         DefaultValue(0)> _
        Public Property Minimum() As Integer
            Get
                Return _barMinimum
            End Get
            Set(ByVal value As Integer)
                If value < _barMaximum Then
                    _barMinimum = value
                    If _trackerValue < _barMinimum Then
                        _trackerValue = _barMinimum
                    If Not ValueChangedEvent Is Nothing Then
                        RaiseEvent ValueChanged(Me, New EventArgs())
                    End If
                    End If
                    Invalidate()
                Else
                    Throw New ArgumentOutOfRangeException("Minimal value is greather than maximal one")
                End If
            End Set
        End Property

    Private _barMaximum As Integer = 200
        ''' <summary>
        ''' Gets or sets the maximum value.
        ''' </summary>
        ''' <value>The maximum value.</value>
        ''' <exception cref="T:System.ArgumentOutOfRangeException">exception thrown when maximal value is lower than minimal one</exception>
    <Description("Set Slider maximal point"), _
     Category("ColorSlider"), _
     DefaultValue(200)> _
    Public Property Maximum() As Integer
        Get
            Return _barMaximum
        End Get
        Set(ByVal value As Integer)
            If value > _barMinimum Then
                _barMaximum = value
                If _trackerValue > _barMaximum Then
                    _trackerValue = _barMaximum
                    If Not ValueChangedEvent Is Nothing Then
                        RaiseEvent ValueChanged(Me, New EventArgs())
                    End If
                End If
                Invalidate()
            Else
                Throw New ArgumentOutOfRangeException("Maximal value is lower than minimal one")
            End If
        End Set
    End Property

        Private _thumbOuterColor As Color = Color.Gray
        ''' <summary>
        ''' Gets or sets the thumb outer color .
        ''' </summary>
        ''' <value>The thumb outer color.</value>
        <Description("Set Slider thumb outer color"), _
         Category("ColorSlider"), _
         DefaultValue(GetType(Color), "Gray")> _
        Public Property ThumbOuterColor() As Color
            Get
                Return _thumbOuterColor
            End Get
            Set(ByVal value As Color)
                _thumbOuterColor = value
                Invalidate()
            End Set
        End Property

        Private _thumbInnerColor As Color = Color.DarkGray
        ''' <summary>
        ''' Gets or sets the inner color of the thumb.
        ''' </summary>
        ''' <value>The inner color of the thumb.</value>
        <Description("Set Slider thumb inner color"), _
         Category("ColorSlider"), _
         DefaultValue(GetType(Color), "DarkGray")> _
        Public Property ThumbInnerColor() As Color
            Get
                Return _thumbInnerColor
            End Get
            Set(ByVal value As Color)
                _thumbInnerColor = value
                Invalidate()
            End Set
        End Property

    Private _thumbPenColor As Color = Color.DimGray
        ''' <summary>
        ''' Gets or sets the color of the thumb pen.
        ''' </summary>
        ''' <value>The color of the thumb pen.</value>
    <Description("Set Slider thumb pen color"), _
     Category("ColorSlider"), _
     DefaultValue(GetType(Color), "DimGray")> _
    Public Property ThumbPenColor() As Color
        Get
            Return _thumbPenColor
        End Get
        Set(ByVal value As Color)
            _thumbPenColor = value
            Invalidate()
        End Set
    End Property

        Private _barColor As Color = Color.Black
        ''' <summary>
        ''' Gets or sets the color of the bar.
        ''' </summary>
        ''' <value>The color of the bar.</value>
        <Description("Set Slider bar inner color"), _
         Category("ColorSlider"), _
         DefaultValue(GetType(Color), "Black")> _
        Public Property BarColor() As Color
            Get
                Return _barColor
            End Get
            Set(ByVal value As Color)
                _barColor = value
                Invalidate()
            End Set
        End Property

    Private _barPenColor As Color = Color.DimGray
        ''' <summary>
        ''' Gets or sets the color of the bar pen.
        ''' </summary>
        ''' <value>The color of the bar pen.</value>
    <Description("Set Slider bar pen color"), _
     Category("ColorSlider"), _
     DefaultValue(GetType(Color), "DimGray")> _
    Public Property BarPenColor() As Color
        Get
            Return _barPenColor
        End Get
        Set(ByVal value As Color)
            _barPenColor = value
            Invalidate()
        End Set
    End Property

        Private _elapseColor As Color = Color.DarkOrange
        ''' <summary>
        ''' Gets or sets the color of the elapsed.
        ''' </summary>
        ''' <value>The color of the elapsed.</value>
        <Description("Set Slider's elapsed part color"), _
         Category("ColorSlider"), _
         DefaultValue(GetType(Color), "DarkOrange")> _
        Public Property ElapsedColor() As Color
            Get
                Return _elapseColor
            End Get
            Set(ByVal value As Color)
                _elapseColor = value
                Invalidate()
            End Set
        End Property

#End Region

#Region "Constructors"

        ''' <summary>
        ''' Initializes a new instance of the <see cref="ColorSlider"/> class.
        ''' </summary>
        ''' <param name="min">The minimum value.</param>
        ''' <param name="max">The maximum value.</param>
        ''' <param name="value">The current value.</param>
        Public Sub New(ByVal min As Integer, ByVal max As Integer, ByVal value As Integer)
            'InitializeComponent()
            SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.ResizeRedraw Or ControlStyles.Selectable Or ControlStyles.SupportsTransparentBackColor Or ControlStyles.UserMouse Or ControlStyles.UserPaint, True)
            BackColor = Color.Transparent

            Minimum = min
            Maximum = max
            value = value
        End Sub
        ''' <summary>
        ''' Initializes a new instance of the <see cref="ColorSlider"/> class.
        ''' </summary>
        Public Sub New()
            Me.New(0, 100, 50)
        End Sub

#End Region

#Region "Paint"

        ''' <summary>
        ''' Raises the <see cref="E:System.Windows.Forms.Control.Paint"></see> event.
        ''' </summary>
        ''' <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"></see> that contains the event data.</param>
        Protected Overloads Overrides Sub OnPaint(ByVal e As PaintEventArgs)
            DrawColorSlider(e, _thumbOuterColor, _thumbInnerColor, _thumbPenColor, _barColor, _barPenColor, _
              _elapseColor)
        End Sub

        ''' <summary>
        ''' Draws the colorslider control using passed colors.
        ''' </summary>
        ''' <param name="e">The <see cref="T:System.Windows.Forms.PaintEventArgs"/> instance containing the event data.</param>
        ''' <param name="thumbOuterColorPaint">The thumb outer color paint.</param>
        ''' <param name="thumbInnerColorPaint">The thumb inner color paint.</param>
        ''' <param name="thumbPenColorPaint">The thumb pen color paint.</param>
        ''' <param name="barInnerColorPaint">The bar color paint.</param>
        ''' <param name="barPenColorPaint">The bar pen color paint.</param>
        ''' <param name="elapsedInnerColorPaint">The elapsed color paint.</param>
        Private Sub DrawColorSlider(ByVal e As PaintEventArgs, ByVal thumbOuterColorPaint As Color, ByVal thumbInnerColorPaint As Color, ByVal thumbPenColorPaint As Color, ByVal barInnerColorPaint As Color, ByVal barPenColorPaint As Color, _
          ByVal elapsedInnerColorPaint As Color)
            Try
                Dim TrackX As Integer = CInt((((_trackerValue - _barMinimum) * (ClientRectangle.Width - _thumbSize)) / (_barMaximum - _barMinimum)))
                _thumbRect = New Rectangle(TrackX, 1, _thumbSize - 1, ClientRectangle.Height - 3)

                'adjust drawing rects
                barRect = ClientRectangle
                thumbHalfRect = _thumbRect
                Dim gradientOrientation As LinearGradientMode
                barRect.Inflate(-1, CInt(-barRect.Height / 3))
                barHalfRect = barRect
                barHalfRect.Height = CInt(barHalfRect.Height / 2)
                gradientOrientation = LinearGradientMode.Vertical
                thumbHalfRect.Height = CInt(thumbHalfRect.Height / 2)
                elapsedRect = barRect
                elapsedRect.Width = CInt(_thumbRect.Left + _thumbSize / 2)
                'get thumb shape path 
                Dim thumbPath As GraphicsPath
                thumbPath = CreateRoundRectPath(_thumbRect, _thumbRoundRectSize)
                'draw bar
                Using lgbBar As New SolidBrush(barInnerColorPaint)
                    e.Graphics.FillRectangle(lgbBar, barRect)
                    'draw elapsed bar
                    Using lgbElapsed As New SolidBrush(elapsedInnerColorPaint)
                        e.Graphics.FillRectangle(lgbElapsed, elapsedRect)
                    End Using
                    'draw bar band                    
                    Using barPen As New Pen(barPenColorPaint, 0.5F)
                        e.Graphics.DrawRectangle(barPen, barRect)
                    End Using
                End Using

                'draw thumb
                Dim newthumbOuterColorPaint As Color = thumbOuterColorPaint, newthumbInnerColorPaint As Color = thumbInnerColorPaint
                Using lgbThumb As New LinearGradientBrush(thumbHalfRect, newthumbOuterColorPaint, newthumbInnerColorPaint, gradientOrientation)
                    lgbThumb.WrapMode = WrapMode.TileFlipXY
                    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
                    e.Graphics.FillPath(lgbThumb, thumbPath)
                    'draw thumb band
                    Dim newThumbPenColor As Color = thumbPenColorPaint
                    Using thumbPen As New Pen(newThumbPenColor)
                        e.Graphics.DrawPath(thumbPen, thumbPath)
                    End Using
                End Using

            Catch Err As Exception
                Console.WriteLine("DrawBackGround Error in " + Name + ":" + Err.Message)
            Finally
            End Try
        End Sub

#End Region

#Region "Overided events"

        ''' <summary>
        ''' Raises the <see cref="E:System.Windows.Forms.Control.EnabledChanged"></see> event.
        ''' </summary>
        ''' <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
        Protected Overloads Overrides Sub OnEnabledChanged(ByVal e As EventArgs)
            MyBase.OnEnabledChanged(e)
            Invalidate()
        End Sub

        ''' <summary>
        ''' Raises the <see cref="E:System.Windows.Forms.Control.MouseDown"></see> event.
        ''' </summary>
        ''' <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        Protected Overloads Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
            MyBase.OnMouseDown(e)
            If e.Button = MouseButtons.Left Then
            If Not ScrollEvent Is Nothing Then
                RaiseEvent Scroll(Me, New ScrollEventArgs(ScrollEventType.ThumbTrack, _trackerValue))
            End If
            If Not ValueChangedEvent Is Nothing Then
                RaiseEvent ValueChanged(Me, New EventArgs())
            End If
                OnMouseMove(e)
            End If
        End Sub

        ''' <summary>
        ''' Raises the <see cref="E:System.Windows.Forms.Control.MouseMove"></see> event.
        ''' </summary>
        ''' <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        Protected Overloads Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
            MyBase.OnMouseMove(e)
            If e.Button = MouseButtons.Left Then
                Dim [set] As ScrollEventType = ScrollEventType.ThumbPosition
                Dim pt As Point = e.Location
                Dim p As Integer = pt.X
                Dim margin As Integer = _thumbSize >> 1
                p -= margin
                Dim coef As Single = CSng((_barMaximum - _barMinimum) / ((ClientSize.Width) - 2 * margin))
                _trackerValue = CInt((p * coef + _barMinimum))

                If _trackerValue <= _barMinimum Then
                    _trackerValue = _barMinimum
                    [set] = ScrollEventType.First
                ElseIf _trackerValue >= _barMaximum Then
                    _trackerValue = _barMaximum
                    [set] = ScrollEventType.Last
                End If

            If Not ScrollEvent Is Nothing Then
                RaiseEvent Scroll(Me, New ScrollEventArgs([set], _trackerValue))
            End If
            If Not ValueChangedEvent Is Nothing Then
                RaiseEvent ValueChanged(Me, New EventArgs())
            End If
            End If
            Invalidate()
        End Sub

        ''' <summary>
        ''' Raises the <see cref="E:System.Windows.Forms.Control.MouseUp"></see> event.
        ''' </summary>
        ''' <param name="e">A <see cref="T:System.Windows.Forms.MouseEventArgs"></see> that contains the event data.</param>
        Protected Overloads Overrides Sub OnMouseUp(ByVal e As MouseEventArgs)
            MyBase.OnMouseUp(e)
        If Not ScrollEvent Is Nothing Then
            RaiseEvent Scroll(Me, New ScrollEventArgs(ScrollEventType.EndScroll, _trackerValue))
        End If
        If Not ValueChangedEvent Is Nothing Then
            RaiseEvent ValueChanged(Me, New EventArgs())
        End If
            Invalidate()
        End Sub

#End Region

#Region "Help routines"

        ''' <summary>
        ''' Creates the round rect path.
        ''' </summary>
        ''' <param name="rect">The rectangle on which graphics path will be spanned.</param>
        ''' <param name="size">The size of rounded rectangle edges.</param>
        ''' <returns></returns>
        Public Shared Function CreateRoundRectPath(ByVal rect As Rectangle, ByVal size As Size) As GraphicsPath
            Dim gp As New GraphicsPath()
            gp.AddLine(CSng(rect.Left + size.Width / 2), CSng(rect.Top), CSng(rect.Right - size.Width / 2), CSng(rect.Top))
            gp.AddArc(rect.Right - size.Width, rect.Top, size.Width, size.Height, 270, 90)

            gp.AddLine(CSng(rect.Right), CSng(rect.Top + size.Height / 2), CSng(rect.Right), CSng(rect.Bottom - size.Width / 2))
            gp.AddArc(rect.Right - size.Width, rect.Bottom - size.Height, size.Width, size.Height, 0, 90)

            gp.AddLine(CSng(rect.Right - size.Width / 2), CSng(rect.Bottom), CSng(rect.Left + size.Width / 2), CSng(rect.Bottom))
            gp.AddArc(rect.Left, rect.Bottom - size.Height, size.Width, size.Height, 90, 90)

            gp.AddLine(CSng(rect.Left), CSng(rect.Bottom - size.Height / 2), CSng(rect.Left), CSng(rect.Top + size.Height / 2))
            gp.AddArc(rect.Left, rect.Top, size.Width, size.Height, 180, 90)
            Return gp
        End Function

        ''' <summary>
        ''' Lightens colors from given array.
        ''' </summary>
        ''' <param name="colorsToLighten">The colors to lighten.</param>
        ''' <returns></returns>
        Public Shared Function LightenColors(ByVal ParamArray colorsToLighten As Color()) As Color()
            Dim colorsToReturn As Color() = New Color(colorsToLighten.Length) {}
            Dim i As Integer = 0
            While i < colorsToLighten.Length
                colorsToReturn(i) = ControlPaint.Light(colorsToLighten(i))
                System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
            End While
            Return colorsToReturn
        End Function

        ''' <summary>
        ''' Sets the trackbar value so that it wont exceed allowed range.
        ''' </summary>
        ''' <param name="val">The value.</param>
        Private Sub SetProperValue(ByVal val As Integer)
            If val < _barMinimum Then
                Value = _barMinimum
            ElseIf val > _barMaximum Then
                Value = _barMaximum
            Else
                Value = val
            End If
        End Sub
#End Region
    End Class

